#if !defined(RESOURCEMANAGER_H)
#define RESOURCEMANAGER_H

#include <hash_map>
#include <string>

#include "resourcetype.h"
#include <Windows.h>
#include "resourceidentifiers.h"

struct IDirect3DDevice9;

namespace gunsmith
{
class CResource;

typedef stdext::hash_map<std::string, CResource*>			RESOURCEMAPBYNAME;	// resource map where name is key and resource* is value
typedef stdext::hash_multimap<ERESOURCETYPE, CResource*>	RESOURCEMAPBYTYPE;	// resource multimap where type is key and resource* is value

/*! \class CResourceManager
	\brief Resource manager is a singleton which provides functions to manage resources. */
class CResourceManager
{
public:
	// non-static member functions
	void						FreeResources		();
	IDirect3DDevice9*			GetDevice			()																				const;	
	bool						GetResourcesByName	(std::string sResName, UINT *pNumRes = NULL, CResource ***pppResArray = NULL);
	void						GetResourcesByType	(ERESOURCETYPE eType, void **ppResArray, int &iSize);
	void						RegisterDevice		(IDirect3DDevice9 *pDevice);

	// template functions
	template<typename T> T*		CreateResource		(const std::string& sResName, void *pSource = NULL);
	template<typename T> T*		GetResourcesByName	(const std::string& sResName);

	// static member functions
	static CResourceManager*	GetInstance			();
	static unsigned int			GetRefCount			();
	static void					Release				();

private:
			CResourceManager	();
	virtual	~CResourceManager	();
	
	static unsigned int		m_iRefCount;
	static CResourceManager	*m_pInstance;
	IDirect3DDevice9		*m_pDevice;
	RESOURCEMAPBYNAME		m_mResName;
	RESOURCEMAPBYTYPE		m_mResType;
	
};

/*!	\brief		Creates an instance of the given type if it doesnt exit and thereby invokes the Create() method.
	\param		sFilename	Source filename.
	\return		CResource	Returns the created resource.
	\remarks	
	\sa			*/
template<typename T> T* CResourceManager::CreateResource(const std::string& sResName, void *pSource)
{
	// look for exsting resources first
	RESOURCEMAPBYNAME::iterator iterFound = m_mResName.find(sResName);
	
	// check for already existing resource
	if(iterFound != m_mResName.end())
	{
#ifdef _DEBUG
		std::string sDebugOutput = "Found requested resource (" + sResName + ") in resource manager.\n";
		//OutputDebugString(sDebugOutput.c_str());
#endif
		return (T*)((*iterFound).second);
	}

	// create a new resource entry
	T *pNewResource = new T;

	if(pNewResource->Create(pSource))
	{
#ifdef _DEBUG
		std::string sDebugOutput = "Create resource (" + sResName + ").\n";
		//OutputDebugString(sDebugOutput.c_str());
#endif
		m_mResName.insert(std::make_pair(sResName, pNewResource));
		m_mResType.insert(std::make_pair(pNewResource->GetResourceType(), pNewResource));
		return pNewResource;
	}
	else
	{
		printf_s("Failed to create resource \"%s\".\n", sResName.c_str());
		delete pNewResource;
		return NULL;
	}
}

template<typename T> T*	CResourceManager::GetResourcesByName(const std::string& sResName)
{
	RESOURCEMAPBYNAME::iterator itFound = m_mResName.find(sResName);

	if(itFound != m_mResName.end())
		return (T*)((*itFound).second);
	else
		return NULL;
}
};

#endif //RESOURCEMANAGER_H